﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;

namespace Upreader.Usenet.Nntp
{

	/// <summary>
	/// Thread safe collection for Stream factories used to building a chain of streams
	/// </summary>
	public class StreamChainBuilder : ICollection<StreamChainBuilderEntry>
    {
        #region Configuration

        public const int UnrelevantPriority = 3;
        public const int MutiblePriority = 2;
        public const int ImmutablePriority = 1;

        #endregion // Configuration

        #region Fields

        private readonly ReaderWriterLockSlim _lock;
		private readonly SortedSet<StreamChainBuilderEntry> _entries;

		#endregion // Fields

		#region Constructor

        public StreamChainBuilder()
            : this(Enumerable.Empty<StreamChainBuilderEntry>())
        {
        }

		public StreamChainBuilder(IEnumerable<StreamChainBuilderEntry> defaultEntries)
		{
			_lock = new ReaderWriterLockSlim();
			_entries = new SortedSet<StreamChainBuilderEntry>(defaultEntries, new StreamChainBuilderEntryComparer());
		}

		#endregion // Constructors

        #region Properties

        public int Count
        {
            get { return _entries.Count; }
        }

        // Always returns false
        public bool IsReadOnly
        {
            get { return false; }
        }

        #endregion // Properties

        #region Methods

        public Stream Build(Stream baseStream)
		{
			Stream currentStream = baseStream;
			IEnumerator<StreamChainBuilderEntry> entryEnumerator = _entries.GetEnumerator();

			_lock.EnterReadLock();
			try
			{
				while (entryEnumerator.MoveNext())
				{
					Func<Stream, Stream> factoryMethod = entryEnumerator.Current.FactoryMethod;

					currentStream = factoryMethod(currentStream);
				}
			}
			finally
			{
				_lock.ExitReadLock();
			}

			return currentStream;
		}

        public void Add(StreamChainBuilderEntry item)
        {
            _lock.EnterWriteLock();
            try
            {
                _entries.Add(item);
            }
            finally
            {
                _lock.ExitWriteLock();
            }
        }

        public void Clear()
        {
            _lock.EnterWriteLock();
            try
            {
                _entries.Clear();
            }
            finally
            {
                _lock.ExitWriteLock();
            }
        }

        public bool Contains(StreamChainBuilderEntry item)
        {
            _lock.EnterReadLock();
            try
            {
                return _entries.Contains(item);
            }
            finally
            {
                _lock.ExitReadLock();
            }
        }

        public void CopyTo(StreamChainBuilderEntry[] array, int arrayIndex)
        {
            _lock.EnterReadLock();
            try
            {
                _entries.CopyTo(array, arrayIndex);
            }
            finally
            {
                _lock.ExitReadLock();
            }
        }

        public bool Remove(StreamChainBuilderEntry item)
        {
            _lock.EnterWriteLock();
            try
            {
                return _entries.Remove(item);
            }
            finally
            {
                _lock.ExitWriteLock();
            }
        }

        /// <summary>
        /// Copies all current entries to a new array prior to returning
        /// </summary>
        /// <returns></returns>
        public IEnumerator<StreamChainBuilderEntry> GetEnumerator()
        {
            _lock.EnterReadLock();
            try
            {
                return (IEnumerator<StreamChainBuilderEntry>)_entries.ToArray().GetEnumerator();
            }
            finally
            {
                _lock.ExitReadLock();
            }
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return (IEnumerator<StreamChainBuilderEntry>)GetEnumerator();
        }

		#endregion // Methods        
    }
}
